//! RK3588 GPIO Controller Driver for Orange Pi 5 Plus
//!
//! This module provides a driver for the RK3588 GPIO controller,
//! supporting GPIO configuration, input/output operations, and interrupts.
//!
//! ## Hardware Information
//! - GPIO0: Base address 0xFD8A0000, IRQ 277 (0x115)
//! - GPIO1: Base address 0xFEC20000, IRQ 278 (0x116)
//! - GPIO2: Base address 0xFEC30000, IRQ 279 (0x117)
//! - GPIO3: Base address 0xFEC40000, IRQ 280 (0x118)
//! - GPIO4: Base address 0xFEC50000, IRQ 281 (0x119)
//!
//! Each GPIO bank contains 32 pins (Port A: 0-7, Port B: 8-15, Port C: 16-23, Port D: 24-31)

use core::ptr::{read_volatile, write_volatile};
use axplat::mem::VirtAddr;
use kspin::SpinNoIrq;
use lazyinit::LazyInit;

/// GPIO pin direction
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpioDirection {
    /// Input mode
    Input,
    /// Output mode
    Output,
}

/// GPIO pull mode
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpioPull {
    /// No pull-up/pull-down
    None,
    /// Pull-up enabled
    PullUp,
    /// Pull-down enabled
    PullDown,
}

/// GPIO interrupt trigger type
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpioTrigger {
    /// No interrupt
    None,
    /// Rising edge trigger
    RisingEdge,
    /// Falling edge trigger
    FallingEdge,
    /// Both edges trigger
    BothEdges,
    /// High level trigger
    HighLevel,
    /// Low level trigger
    LowLevel,
}

/// RK3588 GPIO register offsets
#[repr(C)]
struct Rk3588GpioRegs {
    swport_dr: u32,        // 0x00 - Data register
    swport_ddr: u32,       // 0x04 - Data direction register (0=input, 1=output)
    _reserved1: [u32; 2],  // 0x08-0x0C
    int_en: u32,           // 0x10 - Interrupt enable
    int_mask: u32,         // 0x14 - Interrupt mask
    int_type: u32,         // 0x18 - Interrupt type (0=level, 1=edge)
    int_polarity: u32,     // 0x1C - Interrupt polarity (0=low/falling, 1=high/rising)
    int_bothedge: u32,     // 0x20 - Both edge trigger enable
    int_status: u32,       // 0x24 - Interrupt status (read)
    int_rawstatus: u32,    // 0x28 - Raw interrupt status
    debounce: u32,         // 0x2C - Debounce enable
    porta_eoi: u32,        // 0x30 - Clear interrupt
    ext_port: u32,         // 0x34 - External port (input values)
    _reserved2: [u32; 3],  // 0x38-0x40
    ls_sync: u32,          // 0x44 - Level-sensitive synchronization enable
}

/// RK3588 GPIO Controller
pub struct Rk3588Gpio {
    base_addr: *mut Rk3588GpioRegs,
    gpio_id: u8,
}

impl Rk3588Gpio {
    /// Creates a new GPIO controller instance
    ///
    /// # Arguments
    /// * `base_addr` - Virtual base address of the GPIO controller
    /// * `gpio_id` - GPIO bank ID (0-4)
    pub const fn new(base_addr: VirtAddr, gpio_id: u8) -> Self {
        Self {
            base_addr: base_addr.as_mut_ptr() as *mut Rk3588GpioRegs,
            gpio_id,
        }
    }

    /// Reads a register value
    #[inline]
    fn read_reg(&self, offset: usize) -> u32 {
        unsafe { read_volatile((self.base_addr as *const u32).add(offset)) }
    }

    /// Writes a register value
    #[inline]
    fn write_reg(&mut self, offset: usize, value: u32) {
        unsafe { write_volatile((self.base_addr as *mut u32).add(offset), value) }
    }

    /// Gets the data register value
    #[inline]
    fn swport_dr(&self) -> u32 {
        unsafe { read_volatile(&(*self.base_addr).swport_dr) }
    }

    /// Sets the data register value
    #[inline]
    fn set_swport_dr(&mut self, value: u32) {
        unsafe { write_volatile(&mut (*self.base_addr).swport_dr, value) }
    }

    /// Gets the data direction register value
    #[inline]
    fn swport_ddr(&self) -> u32 {
        unsafe { read_volatile(&(*self.base_addr).swport_ddr) }
    }

    /// Sets the data direction register value
    #[inline]
    fn set_swport_ddr(&mut self, value: u32) {
        unsafe { write_volatile(&mut (*self.base_addr).swport_ddr, value) }
    }

    /// Gets external port value (actual pin state)
    #[inline]
    fn ext_port(&self) -> u32 {
        unsafe { read_volatile(&(*self.base_addr).ext_port) }
    }

    /// Initializes the GPIO controller
    pub fn init(&mut self) {
        // Disable all interrupts initially
        unsafe {
            write_volatile(&mut (*self.base_addr).int_en, 0);
            write_volatile(&mut (*self.base_addr).int_mask, 0xFFFFFFFF);
        }
        debug!("RK3588 GPIO{} initialized at {:#x}", self.gpio_id, self.base_addr as usize);
    }

    /// Configures a GPIO pin
    ///
    /// # Arguments
    /// * `pin` - Pin number (0-31)
    /// * `direction` - Pin direction (Input/Output)
    pub fn set_pin_direction(&mut self, pin: u8, direction: GpioDirection) {
        assert!(pin < 32, "Invalid GPIO pin number");
        
        let mut ddr = self.swport_ddr();
        match direction {
            GpioDirection::Input => ddr &= !(1 << pin),
            GpioDirection::Output => ddr |= 1 << pin,
        }
        self.set_swport_ddr(ddr);
        
        debug!("GPIO{}.{} set to {:?}", self.gpio_id, pin, direction);
    }

    /// Reads the value of a GPIO pin
    ///
    /// # Arguments
    /// * `pin` - Pin number (0-31)
    ///
    /// # Returns
    /// `true` if the pin is high, `false` if low
    pub fn read_pin(&self, pin: u8) -> bool {
        assert!(pin < 32, "Invalid GPIO pin number");
        (self.ext_port() & (1 << pin)) != 0
    }

    /// Writes a value to a GPIO pin
    ///
    /// # Arguments
    /// * `pin` - Pin number (0-31)
    /// * `value` - `true` for high, `false` for low
    pub fn write_pin(&mut self, pin: u8, value: bool) {
        assert!(pin < 32, "Invalid GPIO pin number");
        
        let mut dr = self.swport_dr();
        if value {
            dr |= 1 << pin;
        } else {
            dr &= !(1 << pin);
        }
        self.set_swport_dr(dr);
    }

    /// Toggles a GPIO pin
    ///
    /// # Arguments
    /// * `pin` - Pin number (0-31)
    pub fn toggle_pin(&mut self, pin: u8) {
        assert!(pin < 32, "Invalid GPIO pin number");
        
        let mut dr = self.swport_dr();
        dr ^= 1 << pin;
        self.set_swport_dr(dr);
    }

    /// Sets multiple pins at once using a mask
    ///
    /// # Arguments
    /// * `mask` - Bit mask of pins to set (1 = high, 0 = low)
    /// * `values` - Values for the masked pins
    pub fn write_masked(&mut self, mask: u32, values: u32) {
        let mut dr = self.swport_dr();
        dr = (dr & !mask) | (values & mask);
        self.set_swport_dr(dr);
    }

    /// Reads all 32 pins at once
    pub fn read_all(&self) -> u32 {
        self.ext_port()
    }

    /// Writes all 32 pins at once
    pub fn write_all(&mut self, value: u32) {
        self.set_swport_dr(value);
    }

    /// Configures interrupt for a GPIO pin
    ///
    /// # Arguments
    /// * `pin` - Pin number (0-31)
    /// * `trigger` - Interrupt trigger type
    pub fn set_interrupt(&mut self, pin: u8, trigger: GpioTrigger) {
        assert!(pin < 32, "Invalid GPIO pin number");
        
        let pin_mask = 1u32 << pin;
        
        unsafe {
            let base = self.base_addr;
            let mut int_en = read_volatile(&(*base).int_en);
            let mut int_type = read_volatile(&(*base).int_type);
            let mut int_polarity = read_volatile(&(*base).int_polarity);
            let mut int_bothedge = read_volatile(&(*base).int_bothedge);
            let mut int_mask = read_volatile(&(*base).int_mask);
            
            match trigger {
                GpioTrigger::None => {
                    // Disable interrupt
                    int_en &= !pin_mask;
                    int_mask |= pin_mask;
                }
                GpioTrigger::RisingEdge => {
                    int_type |= pin_mask;         // Edge-triggered
                    int_polarity |= pin_mask;     // Rising edge
                    int_bothedge &= !pin_mask;    // Not both edges
                    int_en |= pin_mask;           // Enable
                    int_mask &= !pin_mask;        // Unmask
                }
                GpioTrigger::FallingEdge => {
                    int_type |= pin_mask;         // Edge-triggered
                    int_polarity &= !pin_mask;    // Falling edge
                    int_bothedge &= !pin_mask;    // Not both edges
                    int_en |= pin_mask;           // Enable
                    int_mask &= !pin_mask;        // Unmask
                }
                GpioTrigger::BothEdges => {
                    int_type |= pin_mask;         // Edge-triggered
                    int_bothedge |= pin_mask;     // Both edges
                    int_en |= pin_mask;           // Enable
                    int_mask &= !pin_mask;        // Unmask
                }
                GpioTrigger::HighLevel => {
                    int_type &= !pin_mask;        // Level-triggered
                    int_polarity |= pin_mask;     // High level
                    int_bothedge &= !pin_mask;    // Not both edges
                    int_en |= pin_mask;           // Enable
                    int_mask &= !pin_mask;        // Unmask
                }
                GpioTrigger::LowLevel => {
                    int_type &= !pin_mask;        // Level-triggered
                    int_polarity &= !pin_mask;    // Low level
                    int_bothedge &= !pin_mask;    // Not both edges
                    int_en |= pin_mask;           // Enable
                    int_mask &= !pin_mask;        // Unmask
                }
            }
            
            write_volatile(&mut (*base).int_type, int_type);
            write_volatile(&mut (*base).int_polarity, int_polarity);
            write_volatile(&mut (*base).int_bothedge, int_bothedge);
            write_volatile(&mut (*base).int_en, int_en);
            write_volatile(&mut (*base).int_mask, int_mask);
        }
        
        debug!("GPIO{}.{} interrupt set to {:?}", self.gpio_id, pin, trigger);
    }

    /// Gets interrupt status
    pub fn get_interrupt_status(&self) -> u32 {
        unsafe { read_volatile(&(*self.base_addr).int_status) }
    }

    /// Clears interrupt for specified pins
    pub fn clear_interrupt(&mut self, pins: u32) {
        unsafe { write_volatile(&mut (*self.base_addr).porta_eoi, pins) }
    }

    /// Enables debounce for specified pins
    pub fn set_debounce(&mut self, pins: u32, enable: bool) {
        unsafe {
            let mut debounce = read_volatile(&(*self.base_addr).debounce);
            if enable {
                debounce |= pins;
            } else {
                debounce &= !pins;
            }
            write_volatile(&mut (*self.base_addr).debounce, debounce);
        }
    }
}

// Safety: The GPIO hardware access is protected by the SpinNoIrq wrapper
unsafe impl Send for Rk3588Gpio {}

/// Global GPIO controller instances
static GPIO0: LazyInit<SpinNoIrq<Rk3588Gpio>> = LazyInit::new();
static GPIO1: LazyInit<SpinNoIrq<Rk3588Gpio>> = LazyInit::new();
static GPIO2: LazyInit<SpinNoIrq<Rk3588Gpio>> = LazyInit::new();
static GPIO3: LazyInit<SpinNoIrq<Rk3588Gpio>> = LazyInit::new();
static GPIO4: LazyInit<SpinNoIrq<Rk3588Gpio>> = LazyInit::new();

/// GPIO pin identifier
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GpioPin {
    /// GPIO bank (0-4)
    pub bank: u8,
    /// Pin number within bank (0-31)
    pub pin: u8,
}

impl GpioPin {
    /// Creates a new GPIO pin identifier
    pub const fn new(bank: u8, pin: u8) -> Self {
        Self { bank, pin }
    }

    /// Creates a GPIO pin from standard notation (e.g., GPIO0_A0 = (0, 0))
    pub const fn from_port_pin(bank: u8, port: u8, pin: u8) -> Self {
        Self {
            bank,
            pin: port * 8 + pin,
        }
    }
}

// Common GPIO pins on Orange Pi 5 Plus
#[allow(dead_code)]
impl GpioPin {
    // Some commonly used GPIO pins
    pub const GPIO0_A0: Self = Self::new(0, 0);
    pub const GPIO0_A1: Self = Self::new(0, 1);
    pub const GPIO0_A2: Self = Self::new(0, 2);
    pub const GPIO0_A3: Self = Self::new(0, 3);
    
    pub const GPIO1_A0: Self = Self::new(1, 0);
    pub const GPIO1_A1: Self = Self::new(1, 1);
    pub const GPIO1_B0: Self = Self::new(1, 8);
    pub const GPIO1_B1: Self = Self::new(1, 9);
    
    pub const GPIO3_A0: Self = Self::new(3, 0);
    pub const GPIO3_B0: Self = Self::new(3, 8);
    pub const GPIO3_C0: Self = Self::new(3, 16);
    pub const GPIO3_D0: Self = Self::new(3, 24);
    
    pub const GPIO4_A0: Self = Self::new(4, 0);
    pub const GPIO4_B0: Self = Self::new(4, 8);
}

/// Gets a reference to the specified GPIO controller
fn get_gpio_controller(bank: u8) -> &'static LazyInit<SpinNoIrq<Rk3588Gpio>> {
    match bank {
        0 => &GPIO0,
        1 => &GPIO1,
        2 => &GPIO2,
        3 => &GPIO3,
        4 => &GPIO4,
        _ => panic!("Invalid GPIO bank: {}", bank),
    }
}

/// Initializes all GPIO controllers
///
/// # Arguments
/// * `gpio_bases` - Array of virtual base addresses for GPIO0-4
pub fn init_all(gpio_bases: &[VirtAddr; 5]) {
    GPIO0.init_once(SpinNoIrq::new(Rk3588Gpio::new(gpio_bases[0], 0)));
    GPIO1.init_once(SpinNoIrq::new(Rk3588Gpio::new(gpio_bases[1], 1)));
    GPIO2.init_once(SpinNoIrq::new(Rk3588Gpio::new(gpio_bases[2], 2)));
    GPIO3.init_once(SpinNoIrq::new(Rk3588Gpio::new(gpio_bases[3], 3)));
    GPIO4.init_once(SpinNoIrq::new(Rk3588Gpio::new(gpio_bases[4], 4)));
    
    GPIO0.lock().init();
    GPIO1.lock().init();
    GPIO2.lock().init();
    GPIO3.lock().init();
    GPIO4.lock().init();
    
    info!("RK3588 GPIO controllers initialized");
}

/// Initializes a single GPIO controller
pub fn init_gpio(bank: u8, base_addr: VirtAddr) {
    let gpio = Rk3588Gpio::new(base_addr, bank);
    match bank {
        0 => {
            GPIO0.init_once(SpinNoIrq::new(gpio));
            GPIO0.lock().init();
        }
        1 => {
            GPIO1.init_once(SpinNoIrq::new(gpio));
            GPIO1.lock().init();
        }
        2 => {
            GPIO2.init_once(SpinNoIrq::new(gpio));
            GPIO2.lock().init();
        }
        3 => {
            GPIO3.init_once(SpinNoIrq::new(gpio));
            GPIO3.lock().init();
        }
        4 => {
            GPIO4.init_once(SpinNoIrq::new(gpio));
            GPIO4.lock().init();
        }
        _ => panic!("Invalid GPIO bank: {}", bank),
    }
}

/// Sets the direction of a GPIO pin
pub fn set_direction(pin: GpioPin, direction: GpioDirection) {
    get_gpio_controller(pin.bank).lock().set_pin_direction(pin.pin, direction);
}

/// Reads the value of a GPIO pin
pub fn read(pin: GpioPin) -> bool {
    get_gpio_controller(pin.bank).lock().read_pin(pin.pin)
}

/// Writes a value to a GPIO pin
pub fn write(pin: GpioPin, value: bool) {
    get_gpio_controller(pin.bank).lock().write_pin(pin.pin, value);
}

/// Toggles a GPIO pin
pub fn toggle(pin: GpioPin) {
    get_gpio_controller(pin.bank).lock().toggle_pin(pin.pin);
}

/// Configures interrupt for a GPIO pin
pub fn set_interrupt(pin: GpioPin, trigger: GpioTrigger) {
    get_gpio_controller(pin.bank).lock().set_interrupt(pin.pin, trigger);
}

/// Gets interrupt status for a GPIO bank
pub fn get_interrupt_status(bank: u8) -> u32 {
    get_gpio_controller(bank).lock().get_interrupt_status()
}

/// Clears interrupt for a GPIO bank
pub fn clear_interrupt(bank: u8, pins: u32) {
    get_gpio_controller(bank).lock().clear_interrupt(pins);
}

/// GPIO interrupt handler
pub fn irq_handler(bank: u8) {
    let controller = get_gpio_controller(bank);
    let mut gpio = controller.lock();
    let status = gpio.get_interrupt_status();
    
    if status != 0 {
        debug!("GPIO{} interrupt: status = {:#x}", bank, status);
        // Clear the interrupts
        gpio.clear_interrupt(status);
        
        // TODO: Call user-registered handlers for each pin
        for pin in 0..32 {
            if (status & (1 << pin)) != 0 {
                debug!("GPIO{}.{} triggered", bank, pin);
            }
        }
    }
}

/// High-level GPIO pin wrapper for safer usage
pub struct GpioPinHandle {
    pin: GpioPin,
}

impl GpioPinHandle {
    /// Creates a new GPIO pin handle
    pub fn new(pin: GpioPin, direction: GpioDirection) -> Self {
        set_direction(pin, direction);
        Self { pin }
    }

    /// Gets the pin identifier
    pub fn pin(&self) -> GpioPin {
        self.pin
    }

    /// Sets the direction
    pub fn set_direction(&mut self, direction: GpioDirection) {
        set_direction(self.pin, direction);
    }

    /// Reads the pin value
    pub fn read(&self) -> bool {
        read(self.pin)
    }

    /// Writes a value to the pin
    pub fn write(&mut self, value: bool) {
        write(self.pin, value);
    }

    /// Sets the pin high
    pub fn set_high(&mut self) {
        write(self.pin, true);
    }

    /// Sets the pin low
    pub fn set_low(&mut self) {
        write(self.pin, false);
    }

    /// Toggles the pin
    pub fn toggle(&mut self) {
        toggle(self.pin);
    }

    /// Checks if the pin is high
    pub fn is_high(&self) -> bool {
        self.read()
    }

    /// Checks if the pin is low
    pub fn is_low(&self) -> bool {
        !self.read()
    }

    /// Configures interrupt
    pub fn set_interrupt(&mut self, trigger: GpioTrigger) {
        set_interrupt(self.pin, trigger);
    }
}

